/* ======================================================================================== *\

		FILE:				InstancedForwardRenderer.cpp
		AUTHOR:				Jack Hoxley

		DESCRIPTION:		An implementation of traditional forward rendering using
							geometry instancing for more efficient rendering.

		CHANGE:				27th August 2007
								Defined basic interface
								Initial implementation
							8th August 2007
								Per-vertex normals now implemented

\* ======================================================================================== */

// Include Standard Headers
// ------------------------
#include "DXUT.h"

#pragma warning( disable : 4995 ) // Not interested in deprectation warnings
#include <sstream>
#pragma warning( default : 4995 )


// Include project headers
// -----------------------
#include "Utils.h"
#include "Profiling.h"
#include "PipeLayout.h"
#include "PipeRenderer.h"
#include "InstancedForwardRenderer.h"
#include "Constants.h"


namespace Renderer
{
	InstancedForwardRenderer::InstancedForwardRenderer( )
	{
		INFO_OUT( L"Creating InstancedForwardRenderer class..." );

		// Initialize member variables
		m_VRAMUsage				= 0;

		m_pEffect				= NULL;

		m_pVertexBuffer			= NULL;
		m_VertexCount			= 0;

		m_pTechnique			= NULL;
		m_ppInputLayouts		= NULL;

		m_pIndexBuffer			= NULL;
		m_IndexCount			= 0;

		m_PipeCount				= 0;

		m_ppPipe				= NULL;
	}



	InstancedForwardRenderer::~InstancedForwardRenderer( )
	{
		INFO_OUT( L"Destroying InstancedForwardRenderer class..." );

		// Probably already called, but make sure:
		DestroyResources( );
	}



	HRESULT InstancedForwardRenderer::CreateResources( ID3D10Device* pDevice, const PipeSim::PipeLayout * const p )
	{
		// 0] General initialization
		// -------------------------
		HRESULT	hr		= S_OK;
		m_VRAMUsage		= 0;

		hr = DestroyResources( );

		if( FAILED( hr ) )
		{
			ERR_OUT( L"Failed to destroy resources before creating new ones." );

			return hr;
		}

		// 1] Create the rendering effect
		// ------------------------------
		UINT uiHLSLFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#ifdef _DEBUG
		uiHLSLFlags |= D3D10_SHADER_DEBUG;
#endif
		UINT uiFXFlags = 0;

		ID3D10Blob* pErrors = NULL;

		hr = D3DX10CreateEffectFromFile( L"ForwardRendering.fx", NULL, NULL, "fx_4_0", uiHLSLFlags, uiFXFlags, pDevice, NULL, NULL, &m_pEffect, &pErrors, NULL );
		if( FAILED( hr ) )
		{
			ERR_OUT( L"Failed to create and compile 'ForwardRendering.fx'" );

			if( NULL != pErrors )
			{
				// We can output any compilation messages
				char* pMessages = reinterpret_cast< char* >( pErrors->GetBufferPointer( ) );

				OutputDebugStringA( pMessages );
			}

			return hr;
		}



		// 2] Create pipe geometry
		// -----------------------
		hr = CreatePipeGeometry( pDevice, p->LayoutXDimension(), p->LayoutYDimension(), p->LayoutZDimension() );

		if( FAILED( hr ) )
		{
			ERR_OUT( L"Failed to create pipe geometry" );

			return hr;
		}



		// 3] Create resources for individual pipes
		// ----------------------------------------
		UINT16 MaximumPipeLength	= p->LayoutXDimension() * p->LayoutYDimension() * p->LayoutZDimension();
		m_PipeCount					= p->NumberOfPipes();
		m_ppPipe					= new PipeResources*[ m_PipeCount ];

		for( UINT16 pipe = 0; pipe < m_PipeCount; ++pipe )
		{
			// Create the actual pipe resources
			m_ppPipe[pipe] = new PipeResources;

			// Initialize the internal states
			m_ppPipe[pipe]->m_LastUpdated		= 0;
			m_ppPipe[pipe]->m_PipeLength		= 0;
			m_ppPipe[pipe]->m_pPerInstanceData	= NULL; // only important if the following code fails!

			// Create the instanced data VB

				// Fill out the description
				D3D10_BUFFER_DESC vbDesc;
				ZeroMemory( &vbDesc, sizeof( D3D10_BUFFER_DESC ) );

				vbDesc.ByteWidth		= MaximumPipeLength * sizeof( InstancedData );
				vbDesc.Usage			= D3D10_USAGE_DYNAMIC;
				vbDesc.BindFlags		= D3D10_BIND_VERTEX_BUFFER;
				vbDesc.CPUAccessFlags	= D3D10_CPU_ACCESS_WRITE;

				m_VRAMUsage += vbDesc.ByteWidth;

				// Create the actual resource
				hr = pDevice->CreateBuffer( &vbDesc, NULL, &m_ppPipe[pipe]->m_pPerInstanceData );
				if( FAILED( hr ) )
				{
					ERR_OUT( L"Failed to create a per-instance data buffer!" );

					return hr;
				}
		}

		return hr;
	}



	HRESULT InstancedForwardRenderer::UpdateResources( const PipeSim::PipeLayout * const p )
	{
		HRESULT hr = S_OK;

		for( UINT16 i = 0; i < p->NumberOfPipes(); ++i )
		{
			// Determine when it was last updated by the simulation
			// If that is after the time we last updated the renderable
			// geometry then we need to flag this pipe as dirty and
			// update accordingly...
			UINT32 LastChanged = p->IterationOfLastChange( i );
			if( LastChanged > m_ppPipe[i]->m_LastUpdated )
			{
				// Simulation is ahead of renderable geometry. This pipe
				// must be updated.
#ifdef DEBUG
				{
					std::wostringstream woss;
					woss << L"Simulation and rendering out of sync for pipe #" << i << L". Simulation: " << p->IterationOfLastChange(i) << L", renderable: " << m_ppPipe[i]->m_LastUpdated;
					Utils::OutputMessage( Utils::Information, WIDEN( __FUNCTION__ ), __LINE__, woss.str().c_str() );
				}
#endif
				// Request an array of coordinates for the current pipe.
				// Do this as a single batch request so as to leave the door open
				// at a later date to implement a multi-threaded architecture...
				//Utils::CoordinateList pCoords;
				Utils::CoordinateList pCoords( p->LayoutXDimension() * p->LayoutYDimension() * p->LayoutZDimension() );
				p->GetPipeCoordinates( i, &pCoords );

				/*
				// DEBUG CODE - REPRODUCIBLE XZ PLANE PATTERN:
				// Top arrow
				pCoords.push_back( Utils::IntegerVector( 2, 0, 1 ) );
				pCoords.push_back( Utils::IntegerVector( 2, 0, 0 ) );
				pCoords.push_back( Utils::IntegerVector( 3, 0, 0 ) );
				pCoords.push_back( Utils::IntegerVector( 3, 0, 1 ) );

				pCoords.push_back( Utils::IntegerVector( 3, 0, 2 ) );

				// Right arrow
				pCoords.push_back( Utils::IntegerVector( 4, 0, 2 ) );
				pCoords.push_back( Utils::IntegerVector( 5, 0, 2 ) );
				pCoords.push_back( Utils::IntegerVector( 5, 0, 3 ) );
				pCoords.push_back( Utils::IntegerVector( 4, 0, 3 ) );

				pCoords.push_back( Utils::IntegerVector( 3, 0, 3 ) );

				// Bottom arrow
				pCoords.push_back( Utils::IntegerVector( 3, 0, 4 ) );
				pCoords.push_back( Utils::IntegerVector( 3, 0, 5 ) );
				pCoords.push_back( Utils::IntegerVector( 2, 0, 5 ) );
				pCoords.push_back( Utils::IntegerVector( 2, 0, 4 ) );

				pCoords.push_back( Utils::IntegerVector( 2, 0, 3 ) );

				// Right arrow
				pCoords.push_back( Utils::IntegerVector( 1, 0, 3 ) );
				pCoords.push_back( Utils::IntegerVector( 0, 0, 3 ) );
				pCoords.push_back( Utils::IntegerVector( 0, 0, 2 ) );
				pCoords.push_back( Utils::IntegerVector( 1, 0, 2 ) );

				pCoords.push_back( Utils::IntegerVector( 2, 0, 2 ) );

				// Start again
				pCoords.push_back( Utils::IntegerVector( 2, 0, 1 ) );
				*/

				// We can now update the pipe with the new coordinates
				//if( FAILED( UpdateIndividualPipe( m_ppPipe[i], pCoords, p->LayoutXDimension(), p->LayoutYDimension(), p->LayoutZDimension(), 20 ) ) )
				if( FAILED( UpdateIndividualPipe( m_ppPipe[i], pCoords, p->LayoutXDimension(), p->LayoutYDimension(), p->LayoutZDimension(), p->PipeLength( i ) ) ) )
				{
					// Not strictly a fatal error, so just issue a debug warning...
					WARN_OUT( L"Unable to update individual pipe!" );
					hr = S_FALSE; // App can continue but something did go wrong!
				}
				else
				{
					// Pipe was successfully updated, so we need to ensure
					// the last updated field is correct.
					m_ppPipe[i]->m_LastUpdated = LastChanged;
					//m_ppPipe[i]->m_PipeLength = 20;
					m_ppPipe[i]->m_PipeLength = p->PipeLength( i );
				}
			}
		}

		return hr;
	}



	HRESULT InstancedForwardRenderer::DestroyResources()
	{
		for( UINT16 i = 0; i < m_PipeCount; ++i )
		{
			// Release resources contained within the struct
			SAFE_RELEASE( m_ppPipe[i]->m_pPerInstanceData );

			// Delete the actual struct
			SAFE_DELETE( m_ppPipe[i] );
		}
		SAFE_DELETE_ARRAY( m_ppPipe );

		if( (NULL != m_pTechnique) && m_pTechnique->IsValid() )
		{
			D3D10_TECHNIQUE_DESC techDesc;
			m_pTechnique->GetDesc( &techDesc );

			for( UINT i = 0; i < techDesc.Passes; ++i )
			{
				SAFE_RELEASE( m_ppInputLayouts[i] );
			}

			SAFE_DELETE_ARRAY( m_ppInputLayouts );
		}

		SAFE_RELEASE( m_pVertexBuffer );
		SAFE_RELEASE( m_pIndexBuffer );

		SAFE_RELEASE( m_pEffect );

		return S_OK;
	}



	HRESULT InstancedForwardRenderer::Render( ID3D10Device* pDevice, D3DXMATRIX& mViewProj )
	{
		// Configure any 'global' effect variables
		m_pEffect->GetVariableByName("mViewProj")->AsMatrix()->SetMatrix( mViewProj );

		// Moving this statement outside of the stateblock stops the following debug warning:
		// [ STATE_SETTING INFO #237: DEVICE_IASETPRIMITIVETOPOLOGY_TOPOLOGY_UNDEFINED ]
		pDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

		// Now perform the actual rendering
		for( UINT16 pipe = 0; pipe < m_PipeCount; ++pipe )
		{
			D3D10_STATE_BLOCK_MASK mask;
			m_pTechnique->ComputeStateBlockMask( &mask );

			D3D10_TECHNIQUE_DESC techDesc;
			m_pTechnique->GetDesc( &techDesc );

			for( UINT pass = 0; pass < techDesc.Passes; ++pass )
			{
				ID3D10StateBlock* pStateBlock = NULL;

				if( SUCCEEDED( D3D10CreateStateBlock( pDevice, &mask, &pStateBlock ) ) )
				{
					if( SUCCEEDED( pStateBlock->Capture() ) )
					{	
						if( SUCCEEDED( m_pTechnique->GetPassByIndex(pass)->Apply(0) ) )
						{
							// Configure the input assembler
							pDevice->IASetInputLayout( m_ppInputLayouts[pass] );
							pDevice->IASetIndexBuffer( m_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );

							UINT strides[] = { sizeof( InstancedVertex ), sizeof( InstancedData ) };
							UINT offsets[] = { 0, 0 };
							ID3D10Buffer* pVBs[] = { m_pVertexBuffer, m_ppPipe[pipe]->m_pPerInstanceData };
							pDevice->IASetVertexBuffers( 0, 2, pVBs, strides, offsets );

							pDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

							// Finally issue the actual draw-call
							UINT IndexCount = ( 1 + Constants::StacksPerPipeSegment ) * (6 * Constants::SlicesPerPipeSegment);
							pDevice->DrawIndexedInstanced( IndexCount, m_ppPipe[pipe]->m_PipeLength, 0, 0, 0 );
						}

						pStateBlock->Apply( );
					}
				}

				SAFE_RELEASE( pStateBlock );
			}
		}

		return S_OK;
	}



	UINT32 InstancedForwardRenderer::VideoMemoryStorageInUse()
	{
		return m_VRAMUsage;
	}



	void InstancedForwardRenderer::RendererName( wchar_t* wcDestination, const size_t& DestinationSize )
	{
		wcscpy_s( wcDestination, DestinationSize, L"Instanced Forward Renderer" );
	}



	HRESULT InstancedForwardRenderer::CreatePipeGeometry( ID3D10Device* pDevice, const UINT16& xSize, const UINT16& ySize, const UINT16& zSize )
	{
		// 1] Initialization
		// -----------------
		HRESULT hr		= S_OK;



		// 2] Define the vertex buffer
		// ---------------------------
		D3D10_BUFFER_DESC vbDesc;
		ZeroMemory( &vbDesc, sizeof( D3D10_BUFFER_DESC ) );

		m_VertexCount = ( 2 + Constants::StacksPerPipeSegment ) * Constants::SlicesPerPipeSegment;

		vbDesc.ByteWidth = m_VertexCount * sizeof( InstancedVertex );
		vbDesc.Usage = D3D10_USAGE_IMMUTABLE;
		vbDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
		vbDesc.CPUAccessFlags = 0;

		m_VRAMUsage += vbDesc.ByteWidth;



		// 3] Generate the contents of the vertex buffer
		// ---------------------------------------------
		D3D10_SUBRESOURCE_DATA vbData;

		// Generate scaling aliases
		float fX = 1.0f / static_cast< float >( xSize );
		float fY = 1.0f / static_cast< float >( ySize );
		float fZ = 1.0f / static_cast< float >( zSize );

		// Default to X in case all dimensions are equal
		float PipeSize = fX;

		// Pick the smallest dimension as the basis for pipe size
		if( (abs(xSize) < abs(ySize)) && (abs(xSize) < abs(zSize)) )
		{
			// X is smallest
			PipeSize = fX;
		}
		else if( (abs(ySize) < abs(xSize)) && (abs(ySize) < abs(zSize)) )
		{
			// Y is  smallest
			PipeSize = fY;
		}
		else if( (abs(zSize) < abs(xSize)) && (abs(zSize) < abs(ySize)) )
		{
			// Z is smallest
			PipeSize = fZ;
		}

		PipeSize *= 0.5f;
		PipeSize *= Constants::MaximumPipeSize;

		CreatePipeVertexData( &vbData, fY, PipeSize );



		// 4] Create the vertex buffer
		// ---------------------------
		hr = pDevice->CreateBuffer( &vbDesc, &vbData, &m_pVertexBuffer );
		if( FAILED( hr ) )
		{
			ERR_OUT( L"Failed to create the vertex buffer!" );

			return hr;
		}

		SAFE_DELETE_ARRAY( vbData.pSysMem );



		// 5] Define the index buffer
		// --------------------------
		D3D10_BUFFER_DESC ibDesc;
		ZeroMemory( &ibDesc, sizeof( D3D10_BUFFER_DESC ) );

		m_IndexCount = ( 1 + Constants::StacksPerPipeSegment ) * Constants::SlicesPerPipeSegment * 6;

		ibDesc.ByteWidth = m_IndexCount * sizeof( UINT32 );
		ibDesc.Usage = D3D10_USAGE_IMMUTABLE;
		ibDesc.BindFlags = D3D10_BIND_INDEX_BUFFER;
		ibDesc.CPUAccessFlags = 0;

		m_VRAMUsage += ibDesc.ByteWidth;



		// 6] Generate the contents of the index buffer
		// --------------------------------------------
		D3D10_SUBRESOURCE_DATA ibData;

		CreatePipeIndexData( &ibData );



		// 7] Create the index buffer
		// --------------------------
		hr = pDevice->CreateBuffer( &ibDesc, &ibData, &m_pIndexBuffer );
		if( FAILED( hr ) )
		{
			ERR_OUT( L"Failed to create the index buffer!" );

			return hr;
		}

		SAFE_DELETE_ARRAY( ibData.pSysMem );



		// 8] Acquire the rendering technique
		// ----------------------------------
		m_pTechnique =  m_pEffect->GetTechniqueByName( "InstancedRendering" );

		if( !m_pTechnique->IsValid() )
		{
			// No technique by this name exists!
			ERR_OUT( L"Unable to acquire a reference to the 'InstancedRendering' technique." );

			return E_NOINTERFACE;
		}



		// 9] Fill in the vertex declaration
		// ---------------------------------
		D3D10_INPUT_ELEMENT_DESC vertex_desc[] = 
		{
			// Per vertex attributes:
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
			{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
			{ "BLEND_WEIGHT", 0, DXGI_FORMAT_R32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 },

			// Rendering properties
			{ "SEGMENT_WEIGHT", 0, DXGI_FORMAT_R32_FLOAT, 1, 0, D3D10_INPUT_PER_INSTANCE_DATA, 1 },

			// First blend matrix
			{ "BLEND_FROM", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_INSTANCE_DATA, 1 },
			{ "BLEND_FROM", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_INSTANCE_DATA, 1 },
			{ "BLEND_FROM", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_INSTANCE_DATA, 1 },
			{ "BLEND_FROM", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_INSTANCE_DATA, 1 },

			// Second blend matrix
			{ "BLEND_TO", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_INSTANCE_DATA, 1 },
			{ "BLEND_TO", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_INSTANCE_DATA, 1 },
			{ "BLEND_TO", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_INSTANCE_DATA, 1 },
			{ "BLEND_TO", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_INSTANCE_DATA, 1 }
		};

		UINT ElementCount = sizeof(vertex_desc) / sizeof( D3D10_INPUT_ELEMENT_DESC );


		// 10] Create the input layout
		// --------------------------
		D3D10_TECHNIQUE_DESC techDesc;
		m_pTechnique->GetDesc( &techDesc );
		m_ppInputLayouts = new ID3D10InputLayout*[ techDesc.Passes ];

		for( UINT i = 0; i < techDesc.Passes; ++i )
		{
			D3D10_PASS_DESC passDesc;
			m_pTechnique->GetPassByIndex(i)->GetDesc( &passDesc );
			hr = pDevice->CreateInputLayout( vertex_desc, ElementCount, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, &m_ppInputLayouts[i] );

			if( FAILED( hr ) )
			{
				ERR_OUT( L"Unable to generate input layout to match pass from technique" );

				return hr;
			}
		}

		return hr;
	}



	void InstancedForwardRenderer::CreatePipeVertexData( D3D10_SUBRESOURCE_DATA* p, const float& height, const float& width )
	{
		UINT current_vertex = 0;
		InstancedVertex *pVerts = new InstancedVertex[ Constants::SlicesPerPipeSegment * (2 + Constants::StacksPerPipeSegment) ];

		for( UINT stack = 0; stack < (2 + Constants::StacksPerPipeSegment); ++stack )
		{
			float s = (static_cast< float >( stack ) / static_cast< float >(1 + Constants::StacksPerPipeSegment));
			float h = (height * s) - (height / 2.0f);

			for( UINT slice = 0; slice < Constants::SlicesPerPipeSegment; ++slice )
			{
				float angle = 2.0f * D3DX_PI * (static_cast< float >( slice ) / static_cast< float >( Constants::SlicesPerPipeSegment ));

				pVerts[current_vertex].p.x = width * sinf(angle);
				pVerts[current_vertex].p.y = h;
				pVerts[current_vertex].p.z = width * cosf(angle);

				pVerts[current_vertex].blend_weight = s;

				pVerts[current_vertex].n = D3DXVECTOR3( sinf(angle), 0.0f, cosf(angle) );

				++current_vertex;
			}
		}

		p->SysMemPitch			= sizeof( InstancedVertex );
		p->pSysMem				= pVerts;
		p->SysMemSlicePitch		= 0;	//unused
	}



	void InstancedForwardRenderer::CreatePipeIndexData( D3D10_SUBRESOURCE_DATA* p )
	{
		UINT32 CurrentIndex = 0;
		UINT32 *pIndices = new UINT32[ 6 * Constants::SlicesPerPipeSegment * (1 + Constants::StacksPerPipeSegment) ];

		for( UINT32 stack = 0; stack < (1 + Constants::StacksPerPipeSegment); ++stack )
		{
			UINT32 StackBaseIndex = stack * Constants::SlicesPerPipeSegment;

			// For each slice we want to create an indexed line
			// between the vertex on this stack and the vertex on the next stack
			for( UINT32 slice = 0; slice < Constants::SlicesPerPipeSegment; ++slice )
			{
				// Create the first triangle
				pIndices[ CurrentIndex + 0 ] = StackBaseIndex + slice;
				pIndices[ CurrentIndex + 1 ] = StackBaseIndex + ((slice + 1) % Constants::SlicesPerPipeSegment) + Constants::SlicesPerPipeSegment;
				pIndices[ CurrentIndex + 2 ] = StackBaseIndex + slice + Constants::SlicesPerPipeSegment;						

				// Create the second triangle
				pIndices[ CurrentIndex + 3 ] = StackBaseIndex + slice;
				pIndices[ CurrentIndex + 4 ] = StackBaseIndex + ((slice + 1) % Constants::SlicesPerPipeSegment);
				pIndices[ CurrentIndex + 5 ] = StackBaseIndex + ((slice + 1) % Constants::SlicesPerPipeSegment) + Constants::SlicesPerPipeSegment;

				CurrentIndex += 6;
			}
		}

		p->SysMemPitch			= sizeof( UINT32 );
		p->pSysMem				= pIndices;
		p->SysMemSlicePitch		= 0;	//unused
	}



	HRESULT InstancedForwardRenderer::UpdateIndividualPipe( PipeResources* p, const Utils::CoordinateList& coords, const UINT16& xSize, const UINT16& ySize, const UINT16& zSize, const UINT32& CurrLen )
	{
		// All we need to do here is update the per-instance
		// information according to the latest pipe layout
		// Scan each pipe in the simulation
		HRESULT hr = S_OK;
		D3DXVECTOR3 mid_point;
		D3DXMATRIX mTemp;
		D3DXMATRIX mFrom;
		D3DXMATRIX mTo;

		// 1] Get access to the per-instance data
		// --------------------------------------
		InstancedData *pInstance = NULL;
		hr = p->m_pPerInstanceData->Map( D3D10_MAP_WRITE_DISCARD, 0, reinterpret_cast< void** >( &pInstance ) );

		if( FAILED( hr ) )
		{
			ERR_OUT( L"Unable to Map() a per-instance data buffer." );

			return hr;
		}

		// 2] For each segment that we have in the current pipe
		// ----------------------------------------------------
		for( UINT32 segment = 0; segment < CurrLen; ++segment )
		{
			// 3] Calculate the mid point for this segment
			// -------------------------------------------
			mid_point.x = ((2.0f * static_cast< float >( coords.at(segment).x ) + 1.0f) / (2.0f * static_cast< float >( xSize ))) - 0.5f;
			mid_point.y = ((2.0f * static_cast< float >( coords.at(segment).y ) + 1.0f) / (2.0f * static_cast< float >( ySize ))) - 0.5f;
			mid_point.z = ((2.0f * static_cast< float >( coords.at(segment).z ) + 1.0f) / (2.0f * static_cast< float >( zSize ))) - 0.5f;

			// 4] Generate FROM blend matrix
			// -----------------------------

				// a] Compute incoming vector
				Utils::IntegerVector InVec;
				if( segment < 1 ) // Can't use (segment-1)<0 due to segment being UINT
				{
					// special case: incoming vector at the first segment should just
					// be a copy of the outgoing vector as there are no previous segments
					InVec.x = coords.at( segment + 1 ).x - coords.at( segment ).x;
					InVec.y = coords.at( segment + 1 ).y - coords.at( segment ).y;
					InVec.z = coords.at( segment + 1 ).z - coords.at( segment ).z;
				}
				else
				{
					// expected case: do a simple (to - from) vector calculation
					InVec.x = coords.at( segment ).x - coords.at( segment - 1 ).x;
					InVec.y = coords.at( segment ).y - coords.at( segment - 1 ).y;
					InVec.z = coords.at( segment ).z - coords.at( segment - 1 ).z;
				}

				// b] Compute rotation matrix
				D3DXMATRIX mIn = GetRotationMatrixForVector( InVec );

				// c] Composite the translation elements
				D3DXMatrixIdentity( &mFrom );
				
				D3DXMatrixTranslation( &mTemp, mid_point.x, mid_point.y, mid_point.z );
				D3DXMatrixMultiply( &mFrom, &mIn, &mTemp );

			// 5] Generate TO blend matrix
			// ---------------------------

				// a] Compute outgoing vector
				Utils::IntegerVector OutVec;
				if( (segment + 1) >= coords.size() )
				{
					// special case: outgoing vector at the last segment has to be the
					// same as the incoming vector as there are no further segments
					OutVec.x = coords.at( segment ).x - coords.at( segment - 1 ).x;
					OutVec.y = coords.at( segment ).y - coords.at( segment - 1 ).y;
					OutVec.z = coords.at( segment ).z - coords.at( segment - 1 ).z;
				}
				else
				{
					// expected case: do a simple (to - from) vector calculation
					OutVec.x = coords.at( segment + 1 ).x - coords.at( segment ).x;
					OutVec.y = coords.at( segment + 1 ).y - coords.at( segment ).y;
					OutVec.z = coords.at( segment + 1 ).z - coords.at( segment ).z;
				}

				// b] Compute rotation matrix
				D3DXMATRIX mOut = GetRotationMatrixForVector( OutVec );

				// c] Composite the translation elements
				D3DXMatrixIdentity( &mTo );
				
				D3DXMatrixTranslation( &mTemp, mid_point.x, mid_point.y, mid_point.z );
				D3DXMatrixMultiply( &mTo, &mOut, &mTemp );

			// 6] Write the actual data
			// ------------------------
			pInstance[segment].segment_weight = static_cast< float >( segment ) / static_cast< float >( CurrLen );
			pInstance[segment].blend_from = mFrom;
			pInstance[segment].blend_to = mTo;
		}

		// 7] Ensure we release the lock on the data
		// -----------------------------------------
		p->m_pPerInstanceData->Unmap();

		return hr;
	}



	D3DXMATRIX InstancedForwardRenderer::GetRotationMatrixForVector( const Utils::IntegerVector& vec )
	{
		D3DXMATRIX mRet;
		D3DXMatrixIdentity( &mRet );

		// Handle vectors going in/out along the X axis
		if( (vec.x != 0) && (vec.y == 0) && (vec.z == 0) )
		{
			if( vec.x < 0 )
			{
				D3DXMatrixRotationZ( &mRet, D3DX_PI / 2.0f );

				/*D3DXMATRIX mTemp;
				D3DXMatrixRotationZ( &mRet, D3DX_PI / 2.0f );
				D3DXMatrixRotationX( &mTemp, D3DX_PI / 2.0f );
				D3DXMatrixMultiply( &mRet, &mRet, &mTemp );*/
			}
			else
			{
				D3DXMatrixRotationZ( &mRet, -D3DX_PI / 2.0f );

				/*D3DXMATRIX mTemp;
				D3DXMatrixRotationZ( &mRet, -D3DX_PI / 2.0f );
				D3DXMatrixRotationX( &mTemp, -D3DX_PI / 2.0f );
				D3DXMatrixMultiply( &mRet, &mRet, &mTemp );*/
			}
		}

		// Handle vectors going in/out along the Y axis
		if( (vec.x == 0) && (vec.y != 0) && (vec.z == 0) )
		{
			if( vec.y < 0 )
			{
				D3DXMatrixScaling( &mRet, 1.0f, -1.0f, 1.0f );
			}
			else
			{
				/* NOP */
			}
		}

		// Handle vectors going in/out along the Z axis
		if( (vec.x == 0) && (vec.y == 0) && (vec.z != 0) )
		{
			if( vec.z < 0 )
			{
				// This is the 
				//   +-
				//   |
				// Joint
				D3DXMatrixRotationX( &mRet, -D3DX_PI / 2.0f );

				/*D3DXMATRIX mTemp;
				D3DXMatrixRotationX( &mRet, -D3DX_PI / 2.0f );
				D3DXMatrixRotationZ( &mTemp, -D3DX_PI );
				D3DXMatrixMultiply( &mRet, &mRet, &mTemp );*/
			}
			else
			{
				// This is the
				//    |
				//   -+
				// Joint
				D3DXMatrixRotationX( &mRet, D3DX_PI / 2.0f );

				/*D3DXMATRIX mTemp;
				D3DXMatrixRotationX( &mRet, D3DX_PI / 2.0f );
				D3DXMatrixRotationZ( &mTemp, D3DX_PI / 2.0f );
				D3DXMatrixMultiply( &mRet, &mRet, &mTemp );*/
			}
		}

		return mRet;
	}
};